/**   
* @Title: FreemarkeRender.java
* @Package com.jbeer.framework.web.viewer.render
* @author Bieber
* @date 2014年6月17日 上午9:59:55
* @version V1.0   
*/

package com.jbeer.framework.web.viewer.render;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jbeer.framework.JBeerWeb;
import com.jbeer.framework.annotation.RefBean;
import com.jbeer.framework.exception.RenderingViewException;
import com.jbeer.framework.logging.Log;
import com.jbeer.framework.utils.LoggerUtil;
import com.jbeer.framework.web.ModelAndView;
import com.jbeer.framework.web.ModelAndView.ViewType;
import com.jbeer.framework.web.viewer.FreemarkerViewer;
import com.jbeer.framework.web.viewrender.Render;

import freemarker.template.Template;

/**
* <p>类功能说明:freemarke渲染器</p>
* <p>类修改者	    修改日期</p>
* <p>修改说明</p>
* <p>Title: FreemarkeRender.java</p>
* @author Bieber <a mailto="bieber.jbeer@hotmail.com">bieber.jbeer@hotmail.com</a>
* @date 2014年6月17日 上午9:59:55
* @version V1.0
*/

public class FreemarkerRender implements Render {

    @RefBean
    private FreemarkerViewer viewer;

    //由于时缓存，所以使用软连接，当jvm内存不够时，将会释放该部分内容
    private volatile SoftReference<ConcurrentHashMap<String,Template>> templateCache = new SoftReference<ConcurrentHashMap<String, Template>>(new ConcurrentHashMap<String, Template>());


    private static final Lock INIT_CACHE_CONTAINER_LOCK = new ReentrantLock();


    private static final Log logger = LoggerUtil.generateLogger(FreemarkerRender.class);

    /* (non-Javadoc)
     * @see com.jbeer.framework.web.viewrender.Render#isSupport(com.jbeer.framework.web.ModelAndView)
     */
    public boolean isSupport(ModelAndView modelView) {
        if(ViewType.PAGE==modelView.getViewType()&&viewer.getFreemarkerConfig()!=null){
            return true;
        }
        return false;
    }

    /* (non-Javadoc)
     * @see com.jbeer.framework.web.viewrender.Render#render(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.jbeer.framework.web.ModelAndView)
     */
    public void render(HttpServletRequest request, HttpServletResponse response,
                       ModelAndView modelView) throws RenderingViewException {
        try {
            if(templateCache ==null|| templateCache.get()==null){//只有为空的时候才需要进行锁控制
                INIT_CACHE_CONTAINER_LOCK.lockInterruptibly();
               try{//由于templateCache是volatile类型，在jvm进行重排序优化后在多线程情况下出现其他线程获取templateCache未完整的实体
                   /**
                    * new SoftReference()可以分为下面几部
                    *men=allocate();//给对象分配内存
                    * templateCache=mem；//将templateCache指向分配的内存地址，注意此时templateCache为非空、
                    *　construction();//调用构造函数
                    *
                    * 如果按照上面顺序执行，将会导致在第二步，其他线程拿到的templateCache不为空，由于此时还没调用构造函数，所以还没有完全初始化好该对象，会导致异常出现
                    * 所以此处templateCache使用volatile，上面执行顺序将是
                    *men=allocate();
                    * construction();
                    *templateCache=mem；
                    * 这样，就不会导致其他线程拿到的时未初始化好的半成品对象了
                    */
                    templateCache =new SoftReference<ConcurrentHashMap<String, Template>>(new ConcurrentHashMap<String, Template>());
               }finally {
                   INIT_CACHE_CONTAINER_LOCK.unlock();
               }
            }
            ConcurrentHashMap<String,Template> templateMap = templateCache.get();
            String templateIdentify = modelView.getView()+JBeerWeb.getViewSuffix();
            Template template = null;
            try{
            if(viewer.isCacheTemplate()){
                if(templateMap.get(templateIdentify)==null){
                    templateMap.putIfAbsent(templateIdentify, viewer.getFreemarkerConfig().getTemplate(templateIdentify));
                }
                template = templateMap.get(templateIdentify);
            }else{
                template = viewer.getFreemarkerConfig().getTemplate(templateIdentify);
            }
            }catch (FileNotFoundException e){
                logger.debug("not found "+templateIdentify+" template from freemarker!");
                return ;
            }
            PrintWriter writer = response.getWriter();
            template.process(modelView.getDatas(), writer);
            writer.flush();
            writer.close();
        } catch (Exception e) {
            throw new RenderingViewException(e);
        }
    }

    /* (non-Javadoc)
     * @see com.jbeer.framework.web.viewrender.Render#order()
     */
    public int order() {
        return 1;
    }

}
